<?php

namespace App\Http\Middleware;

use App\library\services\Jwt;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Predis\Client;
use Illuminate\Support\Facades\Redis;

class Throttle
{
    protected $version = '1.0';//版本号
    protected $time;//时间
    protected $limit;//访问次数

    protected $isOverstep = false;//是否超出请求频率限制

    protected $cache_key;//缓存key

    protected $option = [
        'prefix'=>'',//key前缀
        'key'=>true,//key值
        'visit_rate'=>'',//访问频率
        'visit_method'=>['GET','POST'],//请求类型
        'visit_fail_text'=>'',//访问授权提示文本
        'key_params'=>[]//指定参数 作为key的组合值，
    ];

    /**
     * Author:Yingbo Sun
     * 初始化
     * Throttle constructor.
     * @param Request $request
     */
    public function __construct(Request  $request)
    {
        $this->option = array_merge($this->option,config('throttle'));

    }

    /**
     * Author:Yingbo Sun
     * 处理访问频率
     */
    private function visitRate()
    {

        list($limit,$time) = explode('/',$this->option['visit_rate']);

        $this->limit = $limit;

        if(is_numeric($time)){
            $this->time = $time;
        }else{
            switch ($time){
                case 's':
                    //秒
                    $this->time = 1;
                    break;
                case 'm':
                    //分钟
                    $this->time = 60;
                    break;
                case 'h':
                    //小时
                    $this->time = 3600;
                    break;
                case 'd':
                    //天
                    $this->time = 24 * 3600;
                    break;
                default:
                    break;
            }
        }

    }

    public function setCacheKey($request)
    {
        $ip = $request->ip();
        if($this->option['key'] === true){
            $this->cache_key = $this->option['prefix'].$ip;
        }else{
            $key_val = explode('/',$this->option['key']);
            $cache_key = [];
            if(in_array('__app__',$key_val)) array_push($cache_key,app()->getNamespace());
//            if(in_array('__controller__',$key_val)) array_push($cache_key,$request->route()->action[1][0]);
            if(in_array('__action__',$key_val)) array_push($cache_key,$request->route()->getActionMethod());
            if(in_array('__params__',$key_val))  array_push($cache_key,http_build_query($request->all()));

            if($this->option['key_params']) array_push($cache_key,http_build_query($request->all($this->option['key_params'])));

            if(in_array('__ip__',$key_val))  array_push($cache_key,$ip);

            $this->cache_key = $this->option['prefix'].join('_',$cache_key);
        }

        $this->cache_key = md5($this->cache_key);

    }

    /**
     * Handle an incoming request.
     * Author:Yingbo Sun
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $params = ['key'=>'__app__/__controller__/__action__/__ip__/__params__',];
        //处理额外的配置参数
        $this->option = $params ? array_merge($this->option,$params): $this->option;

        //visit_rate 为null时 表示不限制
        if(!$this->option['visit_rate'])  return $next($request);
        $this->setConfig($request);
        Redis::select(3);
        $check = Redis::get($this->cache_key);
        if($check){
            if( (int)$check >= $this->limit ){
                $this->isOverstep = true;
            }else{
                Redis::incr($this->cache_key);
            }
        }else{
            Redis::set($this->cache_key,1,'EX',$this->time);
        }
        /**
         *  Author:Yingbo Sun
         * 是否超出请求频率限制，
         * 这里根据需要去处理是否抛出异常
         */
        $request->isOverstep = $this->isOverstep;
        if($this->isOverstep){
            $visit_fail_text = str_replace('__WAIT__',$this->time,$this->option['visit_fail_text']);
            throw new \Exception($visit_fail_text);
        }

        $token = $request->header('token');
//        dd($token);
        $data = Jwt::verification($token);
        if ($data['code'] == 2){
            return ['code' => 700,'message' => $data['message']];
        }
        if ($data['code'] == 1){
            $request->uid = decrypt($data['data']->userid);
        }
        return $next($request);
    }


    /**
     * Author:Yingbo Sun
     * 处理自定义参数
     * 缓存key值，key:__app__/__controller__/__action__/__url__/__params__/__data__/__ip__
     * 访问频率visit_rate:1/s
     * @param $request
     */
    public function setConfig($request){

        $this->visitRate();

        $this->setCacheKey($request);
    }
}
